package com.ynding.cloud.common.redis.data.config;

import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;

import java.time.Duration;

/**
 * <p> </p>
 *
 * @author ynding
 * @version 2021/9/8
 **/
@Configuration
public class LettuceRedisConnectionFactory {

    /**
     * cache的数据库索引
     */
    @Value("${redis.cache.database:0}")
    private Integer cacheDatabaseIndex;
    /**
     * sessiondb的数据库索引
     */
    @Value("${redis.session.database:1}")
    private Integer sessionDatabaseIndex;

    @Value("${spring.redis.host:localhost}")
    private String host;

    @Value("${spring.redis.port:6379}")
    private int port;

    @Value("${spring.redis.password:}")
    private String password;

    /**
     * 连接池中的最大空闲连接
     */
    @Value("${spring.redis.lettuce.pool.max-idle:4}")
    private Integer maxIdle;

    /**
     * 连接池中的最小空闲连接
     */
    @Value("${spring.redis.lettuce.pool.min-idle:2}")
    private Integer minIdle;

    /**
     * 连接池最大连接数（使用负值表示没有限制）
     */
    @Value("${spring.redis.lettuce.pool.max-active:4}")
    private Integer maxActive;

    /**
     * 连接池最大阻塞等待时间（使用负值表示没有限制）
     */
    @Value("${spring.redis.lettuce.pool.max-wait:-1}")
    private Long maxWait;

    @Value("${spring.redis.timeout:2000}")
    private Long timeout;

    //#是否缓存空值
    //spring.cache.redis.cache-null-values=false

    /**
     * 在关闭客户端连接之前等待任务处理完成的最长时间，在这之后，无论任务是否执行完成，都会被执行器关闭，默认100ms
     */
    @Value("${spring.redis.lettuce.shutdown-timeout:100}")
    private Long shutdownTimeOut;

    /**
     * connectionFactory.afterPropertiesSet()，初始化的过程中会创建客户端链接资源，在创建过程中，如果重新启动项目，没有回收资源从而导致:
     * DefaultClientResources was not shut down properly
     * 增加了lettuceClientResources 客户端资源由Spring容器管理，并且在销毁的时候调用shutdown方法
     * @return
     */
    @Bean(destroyMethod = "shutdown")
    @ConditionalOnMissingBean(ClientResources.class)
    public DefaultClientResources lettuceClientResources() {
        return DefaultClientResources.create();
    }

    /**
     * 在构建LettuceConnectionFactory时，如果不使用内置的destroyMethod，可能会导致Redis连接早于其它Bean被销毁
     *
     * @return
     */
    @Bean(name = "myLettuceConnectionFactory")
    @Primary
    public RedisConnectionFactory lettuceConnectionFactory(DefaultClientResources lettuceClientResources) {
        //redis配置
        RedisConfiguration redisConfiguration = new RedisStandaloneConfiguration(host, port);
        ((RedisStandaloneConfiguration) redisConfiguration).setDatabase(cacheDatabaseIndex);
        ((RedisStandaloneConfiguration) redisConfiguration).setPassword(RedisPassword.of(password));
        //哨兵模式
        //RedisSentinelConfiguration configuration1 = new RedisSentinelConfiguration();
        //集群模式
        //RedisClusterConfiguration configuration2 = new RedisClusterConfiguration();

        //连接池配置
        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxIdle(maxIdle);
        genericObjectPoolConfig.setMinIdle(minIdle);
        genericObjectPoolConfig.setMaxTotal(maxActive);
        genericObjectPoolConfig.setMaxWaitMillis(maxWait);
        genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(100);

        //redis客户端配置
        LettuceClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()
                .clientResources(lettuceClientResources)
                .commandTimeout(Duration.ofMillis(timeout))
                .shutdownTimeout(Duration.ofMillis(shutdownTimeOut))
                .poolConfig(genericObjectPoolConfig)
                .build();

        //根据配置和客户端配置创建连接
        LettuceConnectionFactory lettuceConnectionFactory = new
                LettuceConnectionFactory(redisConfiguration, lettuceClientConfiguration);
        lettuceConnectionFactory.afterPropertiesSet();

        //是否允许多个线程操作共用同一个缓存连接，默认true，false时每个操作都将开辟新的连接
        //lettuceConnectionFactory.setShareNativeConnection(false);

        return lettuceConnectionFactory;
    }


}
